﻿// <copyright file="Model3DGroup.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media.Media3D
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Markup;
    using System.Collections.Generic;

    /// <summary>
    /// File name: Model3DGroup.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 10/10/2008 11:39:00 h.
    /// Description: Collection of 3-D models.
    ///   <Modifications>
    ///   </Modifications>
    /// </summary>
    [ContentPropertyAttribute("Children")]
    public sealed class Model3DGroup : Model3D
    {
        #region Fields
        /// <summary>
        /// Identifies the Children dependency property.
        /// </summary>
        public static readonly DependencyProperty ChildrenProperty =
                    DependencyProperty.Register(
                          "Children",
                          typeof(Model3DCollection),
                          typeof(Model3DGroup),
                          new PropertyMetadata(OnChildrenPropertyChanged));

        /// <summary>
        /// keep a cached copy of the bounds of this model group.
        /// </summary>
        Rect3D _bounds;
        #endregion
        #region Properties
     
        /// <summary>
        /// Gets or sets a collection of Model3D objects. This is a dependency property.
        /// </summary>
        public Model3DCollection Children
        {
            get
            {

                return (Model3DCollection)this.GetValue(ChildrenProperty);
            }
            set
            {
                this.SetValue(ChildrenProperty, value);
                value.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(value_CollectionChanged);
            }
        }
        #endregion

        public Model3DGroup() : base()
        {
            this.Children = new Model3DCollection();
        }

        #region Methods
        
        /// <summary>
        /// Handle changes on the Children collections.
        /// </summary>
        /// <param name="sender">sender of the event</param>
        /// <param name="e">NotifyCollectionChangedEventArgs with the information of the change.</param>
        void value_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // TODO: I can check if is posible have a Matrix3D sincronized using this event
            // To avoid that every get of Value do a full multiplication of all the inner matrixes.
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                foreach (Model3D item in e.NewItems)
                {
                    item.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(item_PropertyChanged);
                    foreach (ModelVisual3D modelVisual in this.parentWorldTransforms.Keys)
                    {
                        item.SetParentWorldTransform(modelVisual, this.GetLocalToWorldTransform(modelVisual));
                    }
                }
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
            {
                foreach (Model3D item in e.OldItems)
                {
                    item.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            {
                if (e.OldItems != null)
                {
                    foreach (Model3D item in e.OldItems)
                    {
                        item.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(item_PropertyChanged);
                    }
                }
                if (e.NewItems != null)
                {
                    foreach (Model3D item in e.NewItems)
                    {
                        item.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(item_PropertyChanged);
                        foreach (ModelVisual3D modelVisual in this.parentWorldTransforms.Keys)
                        {
                            item.SetParentWorldTransform(modelVisual, this.GetLocalToWorldTransform(modelVisual));
                        }
                    }
                }
            }
            this.NotifyPropertyChanged("Children");
            
        }

        void item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            this.NotifyPropertyChanged("Children");
        }

        /// <summary>
        /// Raise the Children changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnChildrenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Model3DGroup)d).NotifyPropertyChanged("Children");
        }
        #endregion

        protected override Rect3D GetBounds()
        {
            if (this.Transform != null)
            {
                return this.Transform.TransformBounds(_bounds);
            }
            else
            {
                return _bounds;
            }
        }

        internal override void CreateRenderEntities(RenderEntityPool entityPool)
        {
            _bounds = new Rect3D();
            foreach (Model3D model in this.Children)
            {
                model.CreateRenderEntities(entityPool);
                _bounds.Union(model.Bounds);
            }
        }

        internal override bool Render(ModelVisual3D Parent, Axelerate.Silverlight3D.Controls.Viewport3D Viewport, List<RenderEntity> entitiesToRender, Matrix3D worldToScreenMatrix, List<Light> lights)
        {
            bool result = false;
            foreach (Model3D model in this.Children)
            {
                bool modelwasrender = model.Render(Parent, Viewport, entitiesToRender, worldToScreenMatrix, lights);
                result = result || modelwasrender;
            }
            return result;
        }

        internal override void UpdateWorldTransform()
        {
            base.UpdateWorldTransform();

            // Need to update all of the children in the group with the new world transform of the parent;
            if (this.Children != null)
            {
                foreach (ModelVisual3D modelVisual in this.parentWorldTransforms.Keys)
                {
                    foreach (Model3D model in this.Children)
                    {
                        if (modelVisual.IsDirty)
                        {
                            model.SetParentWorldTransform(modelVisual, this.GetLocalToWorldTransform(modelVisual));
                        }
                    }
                }
            }
        }
    }
}
